package my.mashup;

import java.util.*;

public class ApiMap {
    public final Map<Integer, Map<Integer, Double>> weightedMatrix = new HashMap<>();
    public final Map<Integer, Set<Integer>> matrix = new HashMap<>();

    public ApiMap() {
    }

    private void connect(int idA, int idB) {
        // 加权图
        if (weightedMatrix.containsKey(idA)) {
            Map<Integer, Double> aMap = weightedMatrix.get(idA);
            if (aMap.containsKey(idB)) {
                aMap.put(idB, 1 / (1 / aMap.get(idB) + 1));
            } else {
                aMap.put(idB, 1.0);
            }
        } else {
            Map<Integer, Double> aMap = new HashMap<>();
            aMap.put(idB, 1.0);
            weightedMatrix.put(idA, aMap);
        }
        // 无权图
        if (matrix.containsKey(idA)) {
            Set<Integer> aSet = matrix.get(idA);
            aSet.add(idB);
        } else {
            Set<Integer> aSet = new HashSet<>();
            aSet.add(idB);
            matrix.put(idA, aSet);
        }
    }

    // 生成加权图和无权图
    public void genMatrix(Map<String, Api> apiByAddr, Map<String, Set<String>> mashupToAddrs) {
        for (String key : mashupToAddrs.keySet()) {
            // 将同一 Mashup 下的 Api 连接起来
            Set<String> addrSet = mashupToAddrs.get(key);
            List<String> addrs = new ArrayList<>(addrSet);
            for (int i = 0; i < addrs.size() - 1; i++) {
                for (int j = i + 1; j < addrs.size(); j++) {
                    Api a = apiByAddr.get(addrs.get(i));
                    Api b = apiByAddr.get(addrs.get(j));
                    if (a == null || b == null) continue;
                    connect(a.id, b.id);
                    connect(b.id, a.id);
                }
            }
        }
    }

    // 获取两 API 之间的权重（边的权重）
    public double getWeight(int idA, int idB) {
        if (!weightedMatrix.containsKey(idA)) return 0.0;
        Map<Integer, Double> aMap = weightedMatrix.get(idA);
        if (!aMap.containsKey(idB)) return 0.0;
        return aMap.get(idB);
    }

    // 获取无权图两 API 之间的连通性
    public boolean getConnect(int idA, int idB) {
        if (!matrix.containsKey(idA)) return false;
        Set<Integer> aSet = matrix.get(idA);
        return aSet.contains(idB);
    }

    private Set<Api> getApisFromCates(RelMap relMap, Set<String> cates) {
        Set<Api> apis = new HashSet<>();
        for (String cate : cates) {
            Set<Api> tempApis = relMap.apiByCate.get(cate);
            if (tempApis == null) continue;
            apis.addAll(tempApis);
        }
        return apis;
    }

    public Set<Api> searchWeightedMatrix(RelMap relMap, Set<String> cates, Weight weight) {
        Set<Api> ans = new HashSet<>();
        double minWeight = Double.MAX_VALUE;
        // 先找出所有 API
        Set<Api> apis = getApisFromCates(relMap, cates);
        // 对每个 API 分别求解
        for (Api api : apis) {
            // 记录当前已有的类别 set
            Set<String> has = new HashSet<>();
            // 对边维护一个单调数组
            SortedList edges = new SortedList();
            WeightedEdge init = new WeightedEdge(api.id, 0);
            edges.insert(init);
            // 初始值
            Set<Api> nowAns = new HashSet<>();
            double nowWeight = 0.0;
            while (edges.size() > 0) {
                WeightedEdge nowEdge = edges.getFirst();
                Api nowApi = relMap.apiById.get(nowEdge.apiId);
                if (nowAns.contains(nowApi)) continue; // 已经选过这个 Api 了
                nowAns.add(nowApi); // 加入 API
                has.add(nowApi.cate); // 加入类别
                nowWeight += nowEdge.weight; // 加入权重
                if (has.containsAll(cates)) {
                    if (nowWeight < minWeight) {
                        ans = nowAns;
                        minWeight = nowWeight;
                    }
                    break;
                }
                Map<Integer, Double> nowMap = weightedMatrix.get(nowApi.id);
                if (nowMap == null) continue;
                for (Integer id : nowMap.keySet()) { // 读取剩余连通 API
                    WeightedEdge edge = new WeightedEdge(id, nowMap.get(id));
                    edges.insert(edge);
                }
            }
        }
        weight.doubleWeight = minWeight;
        return ans;
    }

    public Set<Api> searchMatrix(RelMap relMap, Set<String> cates, Weight weight) {
        Set<Api> ans = new HashSet<>();
        int minWeight = Integer.MAX_VALUE;
        // 先找出所有 API
        Set<Api> apis = getApisFromCates(relMap, cates);
        // 对每个 API 分别求解
        for (Api api : apis) {
            // 记录当前已有的类别 set
            Set<String> has = new HashSet<>();
            // 对边维护一个普通数组
            List<Integer> edges = new ArrayList<>();
            edges.add(api.id);
            // 初始值
            Set<Api> nowAns = new HashSet<>();
            int nowWeight = 0;
            while (edges.size() > 0) {
                Integer nowId = edges.get(0);
                edges.remove(0);
                Api nowApi = relMap.apiById.get(nowId);
                if (nowAns.contains(nowApi)) continue; // 已经选过这个 Api 了
                nowAns.add(nowApi); // 加入 API
                has.add(nowApi.cate); // 加入类别
                nowWeight += 1; // 加入权重
                if (has.containsAll(cates)) {
                    if (nowWeight < minWeight) {
                        ans = nowAns;
                        minWeight = nowWeight;
                    }
                    break;
                }
                Set<Integer> nowSet = matrix.get(nowApi.id);
                // 读取剩余连通 API
                if (nowSet == null) continue;
                edges.addAll(nowSet);
            }
        }
        weight.intWeight = minWeight;
        return ans;
    }
}
